#include "gpio_control.h"
#include <stdio.h>              // C/C++库：标准输入输出。printf();
#include <stdint.h>
#include <string.h>
#include "math.h"

#include "ohos_init.h"          // OH公共基础库：SYS_RUN();
#include "hi_gpio.h"            // 海思hi3861SDK：hi_gpio_init();hi_gpio_set_dir();hi_gpio_set_ouput_val();HI_GPIO_DIR_OUT;HI_GPIO_VALUE0
#include "hi_io.h"              // 海思hi3861SDK：hi_io_set_func();HI_IO_NAME_GPIO_0;
#include "hi_pwm.h"             // 海思hi3861SDK：hi_pwm_init();hi_pwm_set_clock();hi_pwm_start();HI_PWM_PORT_PWM0;PWM_CLK_XTAL;hi_u16;
#include "hi_adc.h"             // 海思hi3861SDK：hi_adc_read();
#include "iot_pwm.h"            // IOT硬件子系统：IoTPwmInit()
// ADS1X15
#include "ADS1X15.h"
// SSD1306
#include "ssd1306.h"

/*******************************************  
说明：  
二分查找法的优势：查找速度快 1024个长度的表最长只需10次查表就能得出结果
在用NTC测试温度的方案中，NTC的温度表的长度通常是100-200 有些达到400-500的长度 
在这种状况下若是用逐个查表比较的方法来查温度 会致使查表的时间过长，影响程序的 
执行效率 这里推出一个优秀的算法来取代这种最笨的作法 
应用实例以下： 
********************************************/
#define TempSize  100 //定义表长度 
//温度值对应的AD值的表的定义 
const int16_t NTC100K3950[120]={
    334.2264, 317.4508, 301.6157, 286.6631, 272.5389, 259.1926, 246.5770, 234.6482, 223.3650, 212.6890, // 0 ~ 9 ℃
    202.5840, 193.0167, 183.9552, 175.3704, 167.2345, 159.5216, 152.2075, 145.2694, 138.6861, 132.4375, // 10~19 ℃
    126.5049, 120.8705, 115.5180, 110.4316, 105.5969, 101.0000, 96.7127, 92.6306, 88.7426, 85.0386,     // 20~39 ℃
    81.5090, 78.1446, 74.9370, 71.8779, 68.9598, 66.1755, 63.5182, 60.9814, 58.5591, 56.2456,           // 30~39 ℃
    54.0355, 51.9235, 49.9049, 47.9752, 46.1298, 44.3649, 42.6764, 41.0607, 39.5143, 38.0339,           // 40~49 ℃
    36.6163, 35.2587, 33.9582, 32.7121, 31.5178, 30.3731, 29.2755, 28.223, 27.2135, 26.2450,            // 50~59 ℃
    25.3156, 24.4237, 23.5675, 22.7454, 21.9560, 21.1977, 20.4692, 19.7693, 19.0966, 18.4499,           // 60~69 ℃
    17.8282, 17.2304, 16.6554, 16.1023, 15.5702, 15.0581, 14.5652, 14.0907, 13.6339, 13.1940,           // 70~79 ℃
    12.7703, 12.3622, 11.9689, 11.5900, 11.2247, 10.8727, 10.5332, 10.2059, 9.8902, 9.5858,             // 80~89 ℃
    9.2920, 9.0085, 8.7350, 8.4710, 8.2161, 7.9700, 7.7324, 7.5029, 7.2812, 7.0670, 6.8601,             // 90~99 ℃
    6.6601, 6.4668, 6.2799, 6.0993, 5.9246, 5.7557, 5.5923, 5.4343, 5.2814, 5.1334,                     // 100~109 ℃
    4.9903, 4.8517, 4.7175, 4.5877, 4.4619, 4.3401, 4.2222, 4.1079, 3.9972, 3.8900                      // 110~119 ℃
};

/**
 * @brief AD值对应温度值表(升序表)
 * NTC温度传感器R25=100K,分压电阻51K,NTC参考电压3.3V,ADC分辨率12位,ADC参考电压4*1.8
 * 计算方法参考 NTC计算表.excel
 */
const uint16_t NTC100K[100] = {
    0x220, 0x232, 0x243, 0x255, 0x268, 0x27A, 0x28D, 0x29F, 0x2B2, 0x2C5, // 20~39 ℃
    0x2D8, 0x2EB, 0x2FE, 0x311, 0x324, 0x338, 0x34B, 0x35E, 0x371, 0x384, // 30~39 ℃
    0x397, 0x3AA, 0x3BD, 0x3D0, 0x3E2, 0x3F4, 0x407, 0x419, 0x42B, 0x43C, // 40~49 ℃
    0x44E, 0x45F, 0x470, 0x481, 0x492, 0x4A2, 0x4B2, 0x4C2, 0x4D2, 0x4E1, // 50~59 ℃
    0x4F0, 0x4FF, 0x50E, 0x51C, 0x52A, 0x538, 0x546, 0x553, 0x560, 0x56C, // 60~69 ℃
    0x579, 0x585, 0x591, 0x59D, 0x5A8, 0x5B3, 0x5BE, 0x5C8, 0x5D3, 0x5DD, // 70~79 ℃
    0x5E7, 0x5F0, 0x5FA, 0x603, 0x60C, 0x614, 0x61D, 0x625, 0x62D, 0x635, // 80~89 ℃
    0x63C, 0x644, 0x64B, 0x652, 0x659, 0x65F, 0x666, 0x66C, 0x672, 0x678, // 90~99 ℃
    0x67E, 0x684, 0x689, 0x68E, 0x694, 0x699, 0x69D, 0x6A2, 0x6A7, 0x6AB, // 100~109 ℃
    0x6B0, 0x6B4, 0x6B8, 0x6BC, 0x6C0, 0x6C4, 0x6C8, 0x6CB, 0x6CF, 0x6D2, // 110~119 ℃
};

//*************************************  
// 函数名称：ADC转温度  二分查找算法 ->查温度表  
// 函数功能：查找数据在表中对应的位置 表中数据从大到小  
// 入口参数：表地址、表长度、要查找的数据  
// 出口参数：无
// 返 回 值：数据在表中的位置  
//*************************************  
// float AdcConvertTemp(uint16_t *tab, uint8_t tabLong, uint8_t startTemp, uint16_t data)    // 表中数据从大到小  
// {    
//     uint8_t start = 0;                      // 数组头（标号）
//     uint8_t end = tabLong-1;                // 数组尾（标号）
//     uint8_t mid = 0;                        // 数组中间（标号） 
//     // uint8_t i = 0;                          // 防止死循环
//     float temp;

//     if (data >= tab[end]) return 0;
//     else if(data <= tab[start]) return 0;

//     while(start <= end)
//     {
//         mid = (start + end)/2;                                  // 二分取中间值（标号）
                
//         if(data == tab[mid]) break;                             // 情况1:当前值与中间值相等,则正好查询到结果
//         if((tab[mid] > data) && (data > tab[mid+1])) break;     // 情况2:目标值在后半段,且在 mid 和 mid+1 之间
                
//         if(data > tab[mid]) end = mid;                          // 情况3:当前值大于中间值,则目标值在前半部.
//         else start = mid;                                       // 情况4:当前值小于中间值,则目标值在后半段

//         // if(i++ > tabLong) break;                                // 防止死循环
//     }
    
//     temp = mid + (float)(tab[mid]-data)/(float)(tab[mid]-tab[mid+1]); // 计算小数部分
//     temp = startTemp + temp;                                     // 温度表起始温度值
//     return temp;
// }
float AdcConvertTemp(uint16_t *tab, uint8_t tabLong, uint8_t startTemp, uint16_t data)    // 表中数据从小到大  
{    
    uint8_t start = 0;                      // 数组头（标号）
    uint8_t end = tabLong-1;                // 数组尾（标号）
    uint8_t mid = 0;                        // 数组中间（标号） 
    // uint8_t i = 0;                          // 防止死循环
    float temp;

    if (data >= tab[end]) return 0;
    else if(data <= tab[start]) return 0;

    while(start <= end)
    {
        mid = (start + end)/2;                                  // 二分取中间值（标号）
                
        if(data == tab[mid]) break;                             // 情况1:当前值与中间值相等,则正好查询到结果
        if((tab[mid] < data) && (data < tab[mid+1])) break;     // 情况2:目标值在后半段,且在 mid 和 mid+1 之间
                
        if(data > tab[mid]) start = mid;                        // 情况3:当前值大于中间值,则目标值在后半段
        else end = mid;                                         // 情况4:当前值小于中间值,则目标值在前半部

        // if(i++ > tabLong) break;                                // 防止死循环
    }
    
    temp = mid + (float)(tab[mid]-data)/(float)(tab[mid]-tab[mid+1]); // 计算小数部分
    temp = startTemp + temp;                                     // 温度表起始温度值
    return temp;
}

/**
 * @brief GPIO初始化
 * 
 */
void InitGpio(void){
    printf("InitGpio().\n");
    hi_gpio_init();                                                     //GPIO模块初始化
    // hi_gpio_set_dir(HI_IO_NAME_GPIO_10, HI_GPIO_DIR_OUT);               //设置某个GPIO管脚方向
    hi_gpio_set_dir(HI_IO_NAME_GPIO_2, HI_GPIO_DIR_OUT);                //系统控制管脚


    // 使用I2C+ADS1X15获取state
    hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
    hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);
    //ADS1X15_setGain(GAIN_TWOTHIRDS)
    ADS1X15_begin();
}


/**
 * @brief 获取运行状态，输入输出电流电压
 * 
 */
SystemState GetState(OperatingData *operatingData){
    SystemState systemState;
    systemState.errCount = 0;

    operatingData->involtage = 0;
    operatingData->outvoltage = 0;
    operatingData->incurrent = 0;
    operatingData->outcurrent = 0;
    operatingData->temp = 0;

    /* 使用Hi3861自带的ADC获取state */
    // float *data;
    // hi_adc_read(0, &data, HI_ADC_EQU_MODEL_8, HI_ADC_CUR_BAIS_DEFAULT, 0);      // ADC通道0，是A12，输入电压（太阳能端）
    // involtage = (float)(100.0*(hi_adc_convert_to_voltage(data)/0.0248)+0.5)/100.0;
    // hi_adc_read(2, &data, HI_ADC_EQU_MODEL_8, HI_ADC_CUR_BAIS_DEFAULT, 0);      // ADC通道2，是A5，输出电压（储能端）
    // outvoltage = (float)(hi_adc_convert_to_voltage(data)/0.00004);

    /* 使用I2C+ADS1X15获取state */
    // operatingData->involtage = ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(3));
    // operatingData->outvoltage = ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(2));
    // operatingData->incurrent = ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(1));
    // operatingData->outcurrent = ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(0));



    // 采集电压:
    int SamplingCount = 4;  //采样数
    for(int i = 0; i<SamplingCount; i++){                            // 电压传感器平均采样计数 (推荐: 3)
        //TODO：增加ADS1115检测
        operatingData->involtage = operatingData->involtage + ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(0));   //
        operatingData->outvoltage = operatingData->outvoltage + ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(3));   //
        operatingData->incurrent = operatingData->incurrent + ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(1));   // 
        operatingData->outcurrent = operatingData->outcurrent + ADS1X15_computeVolts(ADS1X15_readADC_SingleEnded(2));   // 
    }
    operatingData->involtage  = operatingData->involtage/SamplingCount*40.2857;   //分压系数        
    if (operatingData->involtage < 12)// 如果输入电压低，输入欠压(光照不足)
    {
        systemState.lowInVoltage = true;
        systemState.errCount++;
    }else
    {
        systemState.lowInVoltage = false;
    }
    operatingData->outvoltage  = operatingData->outvoltage/SamplingCount*25;  //分压系数   
    if (operatingData->outvoltage < 5) // 如果输出电压低，未检测到电池
    {
        systemState.lowOutVoltage = true;
        systemState.errCount++;
    }else
    {
        systemState.lowOutVoltage = false;
    }


    // 采集电流:
    operatingData->incurrent  = operatingData->incurrent/SamplingCount;  //
    operatingData->incurrent  = (operatingData->incurrent-2.45)/0.066; //  ACS712供电:4.96V,无电流时,电压为VCC/2.灵敏度0.066A/V
    // if(operatingData->incurrent<0){operatingData->incurrent=0.0000;}        // 电流值为负
    if (operatingData->incurrent > 30)     //输入过流: 电流超过30A
    {
        systemState.overInCurrent = true;
        systemState.errCount++;
    }else
    {
        systemState.overInCurrent = false;
    }
    
    
    // if(operatingData->outvoltage<=0){operatingData->outvoltage = 0.0000;}                       // 如果输出电压<0,那么输出电流 = 0
    // else{operatingData->outcurrent = (operatingData->involtage*operatingData->incurrent)/ operatingData->outvoltage;}    // 否则输出电流 = （输入电压(太阳能电压)*(电池或充电电压)/电池电压
    operatingData->outcurrent  = operatingData->outcurrent/SamplingCount;  //
    operatingData->outcurrent  = (operatingData->outcurrent-2.45)/0.066; // （检测电流v - 电流传感器中点2.525v)*-1 / 电流传感器灵敏度0.066A/V = 得到当前电流输入值。输出功率(电池或充电电压)
    // if(operatingData->outcurrent<0){operatingData->outcurrent=0.0000;}  // 电流值为负
    // report_data->involtage = ((float)((int)((report_data->involtage+0.005)*100)))/100;   // 四啥五入，保留两位小数
    
    


    // 采集温度: 使用Hi3861自带的ADC获取state热敏电阻
    hi_adc_channel_index channel3 = HI_ADC_CHANNEL_3;       // ADC通道编号
    hi_u16 *data;                                           // 读取到的数据保存地址
    hi_adc_equ_model_sel equ_model = HI_ADC_EQU_MODEL_8;    // 平均算法模式：使用8次平均算法模式
    hi_adc_cur_bais cur_bais = HI_ADC_CUR_BAIS_DEFAULT;     // 模拟电源控制：使用默认识别模式，可修改1.8V/3.3V
    hi_u16 delay_cnt = 0;                                   // 从配置采样到启动采样的延时时间计数，一次计数是334ns，其值需在0~0xFF0之间
    hi_adc_read(channel3, &data, equ_model, cur_bais, delay_cnt);       // 从一个ADC通道读一个数据
    hi_float voltage = hi_adc_convert_to_voltage(data);                 // 将ADC读取到的码字转换为电压,(data * 1.8 * 4 / 4096)
    
    // printf("通道0:ADC数据:%d,转换为电压:%.3f.\n",data,voltage);
    voltage = 3.3*51/voltage-51;                                        // 实际电压,供电3.3V,分压电阻51KΩ
    // printf("通道0:ADC数据:%d,转换为电阻:%.3f.\n",data,voltage);
    // operatingData->temp  = 1/((ln(voltage/100)/3950)+1/298.15)-273.15;  // 使用公式计算温度值,不支持ln函数
    operatingData->temp = AdcConvertTemp(NTC100K,100,20,data);      // 使用二分查表法计算温度值

    //printf("通道0:ADC数据:%d,转换为温度:%.3f.\n",data,operatingData->temp);
    if (operatingData->temp > 60 )                                  // 温度超过100℃≈6.6f,80℃≈12.38f
    {
        systemState.overTemperture = true;
        systemState.errCount++;
    }else
    {
        systemState.overTemperture = false;
    }

    //printf("3输入电压:%f,2输出电压:%f,1输入电流:%f,0输出电流:%f,温度电阻:%f\n",operatingData->involtage,operatingData->outvoltage,operatingData->incurrent,operatingData->outcurrent,operatingData->temp);
    // printf("错误数:%d.",systemState.errCount);
    if (systemState.overTemperture){printf("温度过高");}
    if (systemState.overInCurrent){printf("输入过流");}
    if (systemState.overOutCurrent){printf("输出过流");}
    if (systemState.overOutVoltage){printf("输出过压");}
    if (systemState.lowInVoltage){printf("输入欠压(光照不足)");}
    if (systemState.lowOutVoltage){printf("输出欠压(蓄电池未连接)");}
    if (systemState.lowSystemVoltage){printf("系统电压过低");}
    printf("\n");


//   float ff=123.456;
//   double dd=-23456789012.3456;
//   char strff[21],strdd[21];
 
//   memset(strff,0,sizeof(strff)); memset(strdd,0,sizeof(strdd));
 
//   // 把浮点数ff转换为字符串，存放在strff中。
//   sprintf(strff,"%.2f",ff);
//   printf("strff=%s\n",strff);  // 输出[strff=123.46]
 
//   // 把双精度浮点数dd转换为字符串，存放在strdd中。
//   sprintf(strdd,"%.2lf",dd);
//   printf("strdd=%s\n",strdd);  // 输出strdd=123456789012.35
 
//   memset(strff,0,sizeof(strff)); memset(strdd,0,sizeof(strdd));
 
//   // 把浮点数ff转换为字符串，存放在strff中，只保留前10个字符。
//   snprintf(strff,11,"%.2f",ff);
//   printf("strff=%s\n",strff);  // 输出strff=123.46
 
//   // 把双精度浮点数dd转换为字符串，存放在strdd中，只保留前10个字符。
//   snprintf(strdd,11,"%.2lf",dd);
//   printf("strdd=%s\n",strdd);  // 输出strdd=1234567890


    /* 屏幕显示 */
    char strDst0[2] = {0};

    // ssd1306_Fill(Black);
    ssd1306_SetCursor(0, 10);
    ssd1306_DrawString("Error:", Font_7x10, White);
    sprintf_s(strDst0,2,"%d",systemState.errCount);
    ssd1306_DrawString(strDst0, Font_7x10, White);

    char strDst[21];
    ssd1306_SetCursor(0, 20);
    ssd1306_DrawString("involtage :", Font_7x10, White);
    if(operatingData->involtage !=0){                       // TODO:如果为0设备就会重启，原因没查明
        sprintf(strDst,"%.2lf",operatingData->involtage);
    }    
    ssd1306_DrawString(strDst, Font_7x10, White);
memset(strDst,0,sizeof(strDst));
    ssd1306_SetCursor(0, 30);
    ssd1306_DrawString("outvoltage:", Font_7x10, White);
    if(operatingData->involtage !=0){
    sprintf(strDst,"%.2lf",operatingData->outvoltage);}
    ssd1306_DrawString(strDst, Font_7x10, White);
memset(strDst,0,sizeof(strDst));
    ssd1306_SetCursor(0, 40);
    ssd1306_DrawString("incurrent :", Font_7x10, White);
    if(operatingData->involtage !=0){
    sprintf(strDst,"%.2lf",operatingData->incurrent);}
    ssd1306_DrawString(strDst, Font_7x10, White);
memset(strDst,0,sizeof(strDst));
    ssd1306_SetCursor(0, 50);
    ssd1306_DrawString("outcurrent:", Font_7x10, White);
    if(operatingData->involtage !=0){
    sprintf(strDst,"%.2lf",operatingData->outcurrent);}
    ssd1306_DrawString(strDst, Font_7x10, White);

    ssd1306_UpdateScreen();
    
    return systemState;
}

void SystemControl(bool state){
    if (state)
    {
        //printf("SystemControl true.\n");
        hi_gpio_set_ouput_val(HI_IO_NAME_GPIO_2,HI_GPIO_VALUE1);
    }else{
        //printf("SystemControl false.\n");
        hi_gpio_set_ouput_val(HI_IO_NAME_GPIO_2,HI_GPIO_VALUE0);
    }
}
void BuckControl(bool state,int duty){
    if (state)
    {
        //printf("BuckControl true.\n");
        hi_gpio_set_ouput_val(HI_IO_NAME_GPIO_10,HI_GPIO_VALUE1);
        IoTPwmStart(HI_PWM_PORT_PWM0, duty, 39000);//输出频率39KHz
    }else{
        //printf("BuckControl false.\n");
        hi_gpio_set_ouput_val(HI_IO_NAME_GPIO_10,HI_GPIO_VALUE0);
        IoTPwmStart(HI_PWM_PORT_PWM0, duty, 39000);
    }
}

hi_u8 constrain_hi_u8(hi_u8 amt, hi_u8 low, hi_u8 high){        //限幅函数
    return ((amt)<(low)?(low):((amt)>(high)?(high):(amt)));
}

// void predictivePWM(){                                                                //PREDICTIVE PWM ALGORITHM 预测PWM算法
//   if(voltageInput<=0){PPWM=0;}                                                       //Prevents Indefinite Answer when voltageInput is zero 当电压输入为零时，防止无限期回答
//   else{PPWM =(PPWM_margin*pwmMax*voltageOutput)/(100.00*voltageInput);}              //Compute for predictive PWM Floor and store in variable 计算预测PWM地板和存储在变量
//   PPWM = constrain(PPWM,0,pwmMaxLimited);
//   printf("PPWM:%d",PPWM);
// }
/**
 * @brief 预测Buck占空比
 * 
 */
static void PredictiveBuckDuty(void){
    // InitBuckDuty = 100*OutVoltage/InVoltage;
    // InitBuckDuty = constrain_hi_u8(InitBuckDuty,1,99);
    // printf("初始化Buck降压电压占空比：%d。\n",InitBuckDuty);

}



